package sun.sequence.rest;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import sun.sequence.data.Result;
import sun.sequence.dto.SequenceDTO;
import sun.sequence.feign.BusinessNoFeignFeign;
import sun.sequence.model.Sequence;
import sun.sequence.service.SequenceService;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@RestController
public class BusinessNoRest implements BusinessNoFeignFeign {

    private static final Map<String, SequenceDTO> BUSINESS_NO_MAP = new ConcurrentHashMap<>();

    private static final ReentrantLock BUSINESS_NO_MAP_LOCK = new ReentrantLock();

    @Autowired
    private SequenceService sequenceService;

    public Result<Long> genBusinessNo(String namespace, String bizTag) {
        if (log.isDebugEnabled()) {
            log.debug("namespace:{},bizTag:{}", namespace, bizTag);
        }
        Long id = innerGenId(namespace, bizTag);
        if (log.isDebugEnabled()) {
            log.debug("namespace:{},bizTag:{},id:{}", namespace, bizTag, id);
        }
        return Result.success(id);
    }

    public Result<List<Long>> batchGenBusinessNo(String namespace, String bizTag, Integer batchSize) {
        if (log.isDebugEnabled()) {
            log.debug("namespace:{},bizTag:{},batchSize:{}", namespace, bizTag, batchSize);
        }
        List<Long> result = innerGenId(namespace, bizTag, batchSize);
        if (log.isDebugEnabled()) {
            log.debug("namespace:{},bizTag:{},batchSize:{},result:{}", namespace, bizTag, batchSize, JSON.toJSONString(result));
        }
        return Result.success(result);
    }

    public Result<Object> initBusinessNo(String namespace, String bizTag, Long initVal, Integer step, String description) {
        if (initVal == null) {
            initVal = System.currentTimeMillis();
        }
        sequenceService.initSequence(namespace, bizTag, initVal, step, description);
        return Result.success();
    }

    private Long innerGenId(String namespace, String bizTag) {
        List<Long> resultList = innerGenId(namespace, bizTag, 1);
        if (resultList != null && !resultList.isEmpty()) {
            return resultList.get(0);
        }
        log.error("id生成异常:{}", JSON.toJSONString(resultList));
        return null;
    }

    private List<Long> innerGenId(String namespace, String bizTag, Integer batchSize) {

        // 初始化
        String key = namespace + "#" + bizTag;
        long currentTimeMillis = System.currentTimeMillis();
        if (!BUSINESS_NO_MAP.containsKey(key)) {

            Sequence sequence = sequenceService.selectByNamespaceAndBizTag(namespace, bizTag);
            if (sequence == null) {
                initBusinessNo(namespace, bizTag, null, 2000, "系统自动创建");
            }

            try {
                BUSINESS_NO_MAP_LOCK.lock();
                if (!BUSINESS_NO_MAP.containsKey(key)) {
                    Sequence newSequence = sequenceService.generateNewSequence(namespace, bizTag, currentTimeMillis);
                    SequenceDTO sequenceDTO = new SequenceDTO();
                    BeanUtils.copyProperties(newSequence, sequenceDTO);
                    sequenceDTO.setNextId(newSequence.getMaxId());
                    sequenceDTO.setLastModifyTime(currentTimeMillis);

                    BUSINESS_NO_MAP.putIfAbsent(key, sequenceDTO);
                }
            } finally {
                BUSINESS_NO_MAP_LOCK.unlock();
            }
        }

        List<Long> resultList = new ArrayList<>(batchSize);

        SequenceDTO sequenceDTO = BUSINESS_NO_MAP.get(key);
        ReentrantLock sequenceDTOLock = sequenceDTO.getLock();
        try {
            sequenceDTOLock.lock();
            sequenceDTO.setLastModifyTime(currentTimeMillis);
            for (int i = 0; i < batchSize; i++) {
                // 如果nextID >= maxID + step，则更新 maxID
                if (sequenceDTO.getNextId() >= sequenceDTO.getMaxId() + sequenceDTO.getStep()) {
                    Sequence newSequence = sequenceService.generateNewSequence(namespace, bizTag, currentTimeMillis);
                    sequenceDTO.setMaxId(newSequence.getMaxId());
                    sequenceDTO.setStep(newSequence.getStep());
                    sequenceDTO.setNextId(newSequence.getMaxId());
                }
                Long id = sequenceDTO.getNextId();
                sequenceDTO.setNextId(id + 1);

                resultList.add(id);
            }
            return resultList;
        } finally {
            sequenceDTOLock.unlock();
        }
    }

}
